1 /** 2 Copyright: Copyright (c) 2018, Joakim Brännström. All rights reserved. 3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 This module contains the registry of analaysers 7 */ 8 module code_checker.engine.registry; 9 10 import logger = std.experimental.logger; 11 import std.exception : collectException; 12 13 import code_checker.engine.types; 14 15 @safe: 16 17 /// The type of an analyser which then affect the order they are executed. 18 enum Type { 19 staticCode, 20 dynamic, 21 } 22 23 auto makeRegistry() { 24 import code_checker.engine; 25 26 Registry reg; 27 reg.put(new ClangTidy, Type.staticCode); 28 reg.put(new IncludeWhatYouUse, Type.staticCode); 29 return reg; 30 } 31 32 struct Registry { 33 private { 34 BaseFixture[][Type] analysers; 35 } 36 37 void put(BaseFixture a, Type t) { 38 assert(a !is null); 39 40 if (auto v = t in analysers) { 41 (*v) ~= a; 42 } else { 43 analysers[t] = [a]; 44 } 45 } 46 47 /// Range over the analysers. 48 auto range() { 49 import std.array : array; 50 import std.algorithm : map, joiner, filter; 51 52 static immutable order = [Type.staticCode, Type.dynamic]; 53 54 auto getAnalysers(Type t) { 55 if (auto v = t in analysers) 56 return (*v).map!(a => AnalyserRange.Pair(t, a)).array; 57 return null; 58 } 59 60 return order.map!(a => getAnalysers(a)) 61 .filter!(a => a !is null) 62 .joiner 63 .array; 64 } 65 } 66 67 /** Run the `checkers` from `reg` inside `env`. 68 * 69 * Returns: The total status of running the analyzers. 70 */ 71 TotalResult execute(Environment env, string[] analysers, ref Registry reg) @trusted { 72 import std.algorithm; 73 import std.range; 74 75 TotalResult tres; 76 77 void handleResult(Result res_) nothrow { 78 // we know the thread finished and have the only copy. 79 // immutable is a bit cumbersome for now so throw away it to keep the 80 // code somewhat efficient. 81 auto res = cast() res_; 82 83 try { 84 log(res.msg); 85 86 tres.status = mergeStatus(tres.status, res.status); 87 tres.score = Score(tres.score + res.score); 88 tres.supp = Suppressed(tres.supp + res.supp); 89 tres.sugg ~= res.msg.array.filter!(a => a.severity == MsgSeverity.improveSuggestion) 90 .array; 91 tres.failed ~= res.failed; 92 tres.success ~= res.success; 93 94 logger.trace(res); 95 logger.trace(tres); 96 } catch (Exception e) { 97 logger.warning("Failed executing all tests").collectException; 98 logger.warning(e.msg).collectException; 99 tres.status = Status.failed; 100 } 101 } 102 103 foreach (a; reg.range.filter!((a) { 104 if (analysers.empty) 105 return true; 106 return analysers.canFind(a.analyzer.name); 107 })) { 108 logger.infof("%s: %s", a.type, a.analyzer.explain); 109 a.analyzer.putEnv(env); 110 handleResult(executeOneAnalyzer(a.analyzer)); 111 } 112 113 log(tres); 114 return tres; 115 } 116 117 Result executeOneAnalyzer(BaseFixture a) nothrow @trusted { 118 Result r; 119 try { 120 a.setup; 121 a.execute; 122 a.tearDown; 123 r = a.result; 124 } catch (Exception e) { 125 logger.error(e.msg).collectException; 126 r.status = Status.failed; 127 } 128 129 return r; 130 } 131 132 private: 133 134 void log(Messages msgs) { 135 import std.algorithm : sort; 136 137 foreach (m; msgs.value.sort) { 138 final switch (m.severity) { 139 case MsgSeverity.improveSuggestion: 140 break; 141 case MsgSeverity.unableToExecute: 142 case MsgSeverity.failReason: 143 logger.warning(m.value); 144 break; 145 case MsgSeverity.trace: 146 logger.trace(m.value); 147 break; 148 } 149 } 150 } 151 152 void log(TotalResult tres) { 153 import std.conv : to; 154 import colorlog; 155 156 logger.infof("Analyzers reported %s", tres.status == Status.failed 157 ? "Failed".color(Color.red) : "Passed".color(Color.green)); 158 159 if (tres.sugg.length > 0) { 160 logger.info("Suggestions for how to improve the score"); 161 foreach (m; tres.sugg) 162 logger.info(" ", m.value); 163 } 164 165 if (tres.supp != 0) { 166 logger.infof("You suppressed %s warnings", tres.supp); 167 } 168 169 if (tres.status == Status.passed) { 170 logger.info("Congratulations!!!"); 171 } 172 173 string score() { 174 return tres.score < 0 ? tres.score.to!string.color(Color.red) 175 .mode(Mode.bold).toString : tres.score.to!string; 176 } 177 178 logger.infof("You scored %s points", score); 179 } 180 181 /// Input range over the analysers. 182 struct AnalyserRange { 183 import std.typecons : Tuple; 184 185 alias Pair = Tuple!(Type, "type", BaseFixture, "analyzer"); 186 187 Pair[] r; 188 189 auto front() @safe pure nothrow { 190 assert(!empty, "Can't get front of an empty range"); 191 return r[0]; 192 } 193 194 void popFront() @safe pure nothrow { 195 assert(!empty, "Can't pop front of an empty range"); 196 r = r[1 .. $]; 197 } 198 199 bool empty() @safe pure nothrow const @nogc { 200 return r.length == 0; 201 } 202 }